package com.hzw.saas.common.pay.alipay;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.StringUtils;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.hzw.saas.common.pay.alipay.bean.*;
import com.hzw.saas.common.pay.alipay.config.AliPayConfig;
import com.hzw.saas.common.pay.bean.TradeStatus;
import com.hzw.saas.common.pay.enums.PayType;
import com.hzw.saas.common.pay.enums.TradeStatusEnum;
import lombok.extern.slf4j.Slf4j;


/**
 * 支付宝交易执行器
 *
 * @author zzl
 * @since 2020-09-23
 */
@Slf4j
public class AliTradeExecutor {

    /**
     * 页面跳转支付
     *
     * @param content 交易参数
     * @return html跳转代码，写入response进行跳转
     * @throws AlipayApiException
     */
    public static String pagePay(AliPagePayReqContent content) throws AlipayApiException {
        log.debug("ali page pay... content=" + content.buildBizContent());
        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AliPayConfig.config().getReturnUrl());
        alipayRequest.setNotifyUrl(AliPayConfig.config().getNotifyUrl());
        alipayRequest.setBizContent(content.buildBizContent());
        AlipayTradePagePayResponse response = AliPayConfig.client().pageExecute(alipayRequest);
        if (!response.isSuccess()) {
            throw new AlipayApiException(response.getSubCode() + ": " + response.getSubMsg());
        }
        log.debug("ali page pay finished");
        log.debug("ali pay form: {}", response.getBody());
        return response.getBody();
    }

    /**
     * 内嵌二维码支付
     * 使用iframe
     *
     * @param content 交易参数
     * @return 将返回的字符串写入iframe进行跳转显示
     * @throws AlipayApiException
     */
    public static String qrcodePay(AliPagePayReqContent content) throws AlipayApiException {
        log.debug("ali qrcode pay... qrcodeWidth=" + AliPayConfig.config().getQrcodeWidth());
        content.qr_pay_mode("4").qrcode_width(AliPayConfig.config().getQrcodeWidth());
        return pagePay(content);
    }

    /**
     * 内嵌二维码支付
     *
     * @param content     交易参数
     * @param qrcodeWidth 二维码宽度 px
     * @return 将返回的字符串写入iframe进行跳转显示
     */
    public static String qrcodePay(AliPagePayReqContent content, int qrcodeWidth) throws AlipayApiException {
        log.debug("ali qrcode pay... qrcodeWidth=" + qrcodeWidth);
        content.qr_pay_mode("4").qrcode_width(String.valueOf(qrcodeWidth));
        return pagePay(content);
    }

    /**
     * 交易查询
     * <p>
     * 支付宝交易状态主动查询接口
     * 特殊使用场景
     * - 当商户后台、网络、服务器等出现异常，商户系统最终未接收到支付通知
     * - 调用支付接口后，返回系统错误或未知交易状态情况
     * - 调用alipay.trade.pay，返回INPROCESS的状态
     * - 调用alipay.trade.cancel之前，需确认支付状态
     *
     * @param content 交易参数
     * @return
     * @throws AlipayApiException
     */
    public static TradeStatus query(AliQueryReqContent content) throws AlipayApiException {
        log.debug("alipay query... content=" + content.buildBizContent());
        //设置请求参数
        AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
        alipayRequest.setBizContent(content.buildBizContent());

        //请求
        AlipayTradeQueryResponse response = AliPayConfig.client().execute(alipayRequest);
        if (!response.isSuccess()) {
            throw new AlipayApiException(response.getSubCode() + ": " + response.getSubMsg());
        }

        TradeStatus tradeStatus = new TradeStatus(response.getOutTradeNo(), response.getTradeNo(), response.getSendPayDate());
        tradeStatus.setPayType(PayType.ALI_PAY);
        tradeStatus.setTradeStatus(TradeStatusEnum.getByName(response.getTradeStatus()));
        log.debug("alipay query finished... status=" + tradeStatus);
        return tradeStatus;
    }

    /**
     * 交易撤销
     * <p>
     * 支付交易返回失败或支付系统超时，调用该接口撤销交易
     * 如果此订单用户支付失败，支付宝系统会将此订单关闭；如果用户支付成功，支付宝系统会将此订单资金退还给用户
     * 注意：只有发生支付系统超时或者支付结果未知时可调用撤销，其他正常支付的单如需实现相同功能请调用申请退款API。提交支付交易后调用【查询订单API】，没有明确的支付结果再调用【撤销订单API】
     * <p>
     * 注意：当用次接口导致退款时，无法通过refundQuery查询到退款信息
     *
     * @param content 交易参数
     * @return refundSuccess|tradeClosed|unknown status
     * @throws AlipayApiException
     */
    public static TradeStatus cancel(AliCancelReqContent content) throws AlipayApiException {
        log.debug("alipay cancel... content=" + content.buildBizContent());
        AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();
        request.setBizContent(content.buildBizContent());
        AlipayTradeCancelResponse response = AliPayConfig.client().execute(request);
        if (!response.isSuccess()) {
            throw new AlipayApiException(response.getSubCode() + ": " + response.getSubMsg());
        }

        TradeStatus tradeStatus = new TradeStatus(response.getOutTradeNo(), response.getTradeNo());
        tradeStatus.setPayType(PayType.ALI_PAY);
        if (StringUtils.isEmpty(response.getAction())) {
            response.setAction("");
        }
        switch (response.getAction()) {
            case "refund": //交易已支付，触发交易退款动作
                tradeStatus.setTradeStatus(TradeStatusEnum.REFUND_SUCCESS);
                break;
            case "close": //交易未支付，触发关闭交易动作，无退款
                tradeStatus.setTradeStatus(TradeStatusEnum.TRADE_CLOSED);
                break;
            case "":
            default:
                tradeStatus.setTradeStatus(TradeStatusEnum.UNKNOWN);
        }
        log.debug("alipay cancel finished... status=" + tradeStatus);
        return tradeStatus;
    }

    /**
     * 交易关闭
     * 交易创建后，用户在一定时间内未进行支付，调用该接口直接将未付款的交易进行关闭。
     * 若交易已支付完成，则会抛出异常
     *
     * @param content
     * @return tradeClosed status
     * @throws AlipayApiException
     */
    public static TradeStatus close(AliCloseReqContent content) throws AlipayApiException {
        log.debug("alipay close... content=" + content.buildBizContent());
        AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
        request.setBizContent(content.buildBizContent());
        AlipayTradeCloseResponse response = AliPayConfig.client().execute(request);
        if (!response.isSuccess()) {
            throw new AlipayApiException(response.getSubCode() + ": " + response.getSubMsg());
        }
        TradeStatus tradeStatus = new TradeStatus(response.getOutTradeNo(), response.getTradeNo());
        tradeStatus.setPayType(PayType.ALI_PAY);
        tradeStatus.setTradeStatus(TradeStatusEnum.TRADE_CLOSED);
        log.debug("alipay close finished... status=" + tradeStatus);
        return tradeStatus;
    }

    /**
     * 交易退款
     * 支付宝将在收到退款请求并且验证成功之后，按照退款规则将支付款按原路退到买家帐号上
     * 交易超过约定时间（签约时设置的可退款时间）的订单无法进行退款
     * 总退款金额不能超过用户实际支付金额
     *
     * @param content
     * @return
     * @throws AlipayApiException
     */
    public static TradeStatus refund(AliRefundReqContent content) throws AlipayApiException {
        log.debug("alipay refund... content=" + content.buildBizContent());
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        request.setBizContent(content.buildBizContent());
        AlipayTradeRefundResponse response = AliPayConfig.client().execute(request);
        if (!response.isSuccess()) {
            throw new AlipayApiException(response.getSubCode() + ": " + response.getSubMsg());
        }

        TradeStatus tradeStatus = new TradeStatus(response.getOutTradeNo(), response.getTradeNo());
        tradeStatus.setPayType(PayType.ALI_PAY);
        if ("Y".equals(response.getFundChange())) {
            tradeStatus.setTradeStatus(TradeStatusEnum.REFUND_SUCCESS);
        } else {
            tradeStatus.setTradeStatus(TradeStatusEnum.REFUND_FAIL);
        }
        log.debug("alipay refund finished... status=" + tradeStatus);
        return tradeStatus;
    }

    /**
     * 退款查询接口
     * 查询自已通过alipay.trade.refund或alipay.trade.refund.apply提交的退款请求是否执行成功
     *
     * @param content
     * @return
     * @throws AlipayApiException
     */
    public static TradeStatus refundQuery(AliRefundQueryReqContent content) throws AlipayApiException {
        log.debug("alipay refundQuery... content=" + content.buildBizContent());
        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
        request.setBizContent(content.buildBizContent());
        AlipayTradeFastpayRefundQueryResponse response = AliPayConfig.client().execute(request);
        if (!response.isSuccess()) {
            throw new AlipayApiException(response.getSubCode() + ": " + response.getSubMsg());
        }

        System.out.println(response.getBody());
        TradeStatus tradeStatus = new TradeStatus(response.getOutTradeNo(), response.getTradeNo());
        tradeStatus.setPayType(PayType.ALI_PAY);
        // TODO: ZZL@2020-09-25 优化判断机制
        if (!StringUtils.isEmpty(response.getRefundAmount())) {
            tradeStatus.setTradeStatus(TradeStatusEnum.REFUND_SUCCESS);
        } else {
            tradeStatus.setTradeStatus(TradeStatusEnum.UNKNOWN);
        }
        log.debug("alipay refund refundQuery... status=" + tradeStatus);
        return tradeStatus;
    }

}
